package com.recharge.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.recharge.common.domain.PageDO;
import com.recharge.common.enums.ErrorCode;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.*;
import com.recharge.core.entity.MerchantAccountEntity;
import com.recharge.core.entity.MerchantBalanceEntity;
import com.recharge.core.mapper.MerchantAccountMapper;
import com.recharge.core.mapper.MerchantBalanceMapper;
import com.recharge.core.mapper.TransactionMapper;
import com.recharge.domain.query.MerchantAccountQueryDO;
import com.recharge.domain.vo.MerchantAccountDO;
import com.recharge.service.MerchantAccountService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author reagan
 * @since 2020-08-19
 */
@Service
public class MerchantAccountServiceImpl extends ServiceImpl<MerchantAccountMapper, MerchantAccountEntity> implements MerchantAccountService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    private TransactionMapper transactionMapper;
    @Autowired
    private MerchantBalanceMapper merchantBalanceMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional(propagation= Propagation.REQUIRED, rollbackFor= Exception.class)
    public ResultDO add(String merchantId) {

        MerchantAccountEntity entity = new MerchantAccountEntity();

        ResultDO result = new ResultSupport();

        entity.setId(merchantId);
        entity.setAccountNo(SnowflakeIdWorker.getId());
        entity.setCreateDate(Calendar.getInstance().getTime());
        entity.setModifiDate(entity.getCreateDate());
        entity.setModifier(entity.getCreator());
        entity.setBalance(0l);
        entity.setCredit(0l);
        boolean res = false;
        try {
            res = this.save(entity);
        } catch(Exception e) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }

        //判断r的值是否小于0.如果小于0 就代表添加失败
        if(!res) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.MERCHANT_ACCOUNT_ADD_ERROR);
            logger.debug("error code：" + ErrorCode.MERCHANT_ACCOUNT_ADD_ERROR.getCode() + " msg" + ErrorCode.MERCHANT_ACCOUNT_ADD_ERROR.getMsg());
            return result;
        }
        MerchantAccountDO domain = getDomain(entity);
        redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + domain.getId(), domain);
        return result;
    }

    @Transactional(propagation= Propagation.REQUIRED, rollbackFor= Exception.class)
    public ResultDO edit(MerchantAccountDO domain) {

        MerchantAccountEntity entity = new MerchantAccountEntity();

        ResultDO result = BeanUtilsExtends.copy(entity, domain);
        if (!result.isSuccess()) {
            logger.debug("error code：" + result.getErrorCode().getCode() + " msg" + result.getErrorCode().getMsg());
            return result;
        }

        entity.setModifiDate(Calendar.getInstance().getTime());

        boolean res = false;
        try {
            res = this.updateById(entity);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }

        //判断r的值是否小于0.如果小于0 就代表添加失败
        if(!res) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.MERCHANT_ACCOUNT_EDIT_ERROR);
            logger.debug("error code：" + ErrorCode.MERCHANT_ACCOUNT_EDIT_ERROR.getCode() + " msg" + ErrorCode.MERCHANT_ACCOUNT_EDIT_ERROR.getMsg());
            return result;
        }

        return result;
    }


    @Override
    public ResultDO editBalance(String id, Long amount) {

        ResultDO result = new ResultSupport();
       /* MerchantAccountEntity entity = null;
        try {
            entity = this.baseMapper.selectById(id);
        } catch (Exception e) {
            result.setSuccess(false);
            e.printStackTrace();
        }*/

        long beforeBalance = 0l;
        String lockKey = "LOCK_COURSE" + id;
        RLock lock = redissonClient.getLock(lockKey);
        boolean is = true;
        try {
            if (lock.tryLock(30, TimeUnit.SECONDS)) {
                MerchantAccountDO domain = (MerchantAccountDO) redisUtil.get(Constant.MERCHANT_ACCOUNT + ":" + id);
                beforeBalance = domain.getBalance();
                logger.debug("交易前余额:" + beforeBalance + " 交易后余额:" + domain.getBalance());
                long credit = -domain.getCredit();
                if ((beforeBalance + amount) < credit) {
                    is = false;
                } else {
                    domain.setBalance(beforeBalance + amount);
                }
                redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + id, domain);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(lock != null) {
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }

        if(!is) {
            result.setSuccess(false);
            logger.debug("支付扣款失败，商户余额不足");
            return result;
        }

       /* MerchantBalanceEntity merchantBalanceEntity = new MerchantBalanceEntity();
        merchantBalanceEntity.setAmount(amount);
        merchantBalanceEntity.setMerchantId(id);
        try {
            merchantBalanceMapper.insert(merchantBalanceEntity);
        } catch (Exception e) {
            e.printStackTrace();
            result.setSuccess(false);
        }*/

        //redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + domain.getId(), domain);
        result.setModel(ResultDO.FIRST_MODEL_KEY, beforeBalance);
        result.setModel(ResultDO.SECOND_MODEL_KEY, beforeBalance + amount);
        return result;
    }

    @Override
    public void syncMerchantAccount() {
        while(!Thread.currentThread().isInterrupted()) {
            List<MerchantAccountEntity> list = null;
            try {
                list = this.baseMapper.selectList(Wrappers.<MerchantAccountEntity>lambdaQuery().
                        eq(MerchantAccountEntity::getIsDelete, Constant.IS_DELETE_FALSE).
                        eq(MerchantAccountEntity::getStatus, 1));
            } catch (Exception e) {
                e.printStackTrace();
            }

            for(MerchantAccountEntity entity : list) {

                String lockKey = "LOCK_COURSE" + entity.getAccountNo();
                RLock lock = redissonClient.getLock(lockKey);
                try {
                    if (lock.tryLock(10, TimeUnit.SECONDS)) {
                        MerchantAccountDO cacheDomain = (MerchantAccountDO) redisUtil.get(Constant.MERCHANT_ACCOUNT + ":" + entity.getId());
                        if (cacheDomain != null) {
                            Long balance = cacheDomain.getBalance();
                            if (entity.getBalance().longValue() != balance.longValue()) {
                                MerchantAccountEntity updateEntity = new MerchantAccountEntity();
                                logger.debug("============更改的余额：" + cacheDomain.getBalance() + " 旧的余额：" + entity.getBalance());
                                updateEntity.setId(entity.getId());
                                updateEntity.setBalance(cacheDomain.getBalance());
                                int r = baseMapper.updateById(updateEntity);
                                logger.debug("同步账户余额" + r + ":" + updateEntity);
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
                } finally {
                    if(lock != null) {
                        if (lock.isHeldByCurrentThread()) {
                            lock.unlock();
                        }
                    }
                }
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public ResultDO addCredit(String id, Long amount, Long noAmount) {

        long beforeMoney = 0;
        long afterMoney = 0;
        ResultDO result = new ResultSupport();

        MerchantAccountDO cacheDomain = (MerchantAccountDO) redisUtil.get(Constant.MERCHANT_ACCOUNT + ":" + id);
        if(cacheDomain == null) {
            result = get(id);
            if (!result.isSuccess()) {
                logger.debug("error code：" + result.getErrorCode().getCode() + " msg" + result.getErrorCode().getMsg());
                return result;
            }

            cacheDomain = (MerchantAccountDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
//            redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + cacheDomain.getId(), cacheDomain);
        }

        MerchantAccountEntity entity = new MerchantAccountEntity();
        entity.setId(cacheDomain.getId());
        if (amount != null){
            beforeMoney = cacheDomain.getCredit();
            if (cacheDomain.getBalance() < -amount){
                result.setSuccess(false);
                result.setErrorCode(ErrorCode.API_USER_BALANCE_STATUS_ERROR);
                logger.debug("余额不足,无法调整固定额度");
                return result;
            }
            afterMoney = amount;
            entity.setCredit(amount);
            cacheDomain.setCredit(amount);
        }
        if (noAmount != null){
            beforeMoney = cacheDomain.getNoAmount();
            afterMoney = cacheDomain.getNoAmount() + noAmount;
            entity.setNoAmount(afterMoney);
            cacheDomain.setNoAmount(afterMoney);
        }
        // 更新redis
        redisUtil.set(Constant.MERCHANT_ACCOUNT + ":" + cacheDomain.getId(), cacheDomain);
        int update = 0;
        try {
            // 更新数据库
            update = baseMapper.updateById(entity);
        }catch (Exception e){
            logger.debug(e.getMessage());
        }

        if (update <= 0) {
            result.setSuccess(false);
        }

        result.setModel(ResultDO.FIRST_MODEL_KEY, beforeMoney);
        result.setModel(ResultDO.SECOND_MODEL_KEY, afterMoney);
        return result;
    }


    public ResultDO get(String id) {

        ResultDO result = new ResultSupport();
        MerchantAccountEntity entity = null;

        try {
            entity = this.getById(id);
        } catch(Exception e) {
            e.printStackTrace();
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }

        MerchantAccountDO domain = getDomain(entity);

        result.setModel(ResultDO.FIRST_MODEL_KEY, domain);

        return result;
    }

    @Override
    public ResultDO listall() {

        ResultDO result = new ResultSupport();

        List<MerchantAccountEntity> list = null;

        try {
            list = this.baseMapper.selectList(Wrappers.<MerchantAccountEntity>lambdaQuery().eq(MerchantAccountEntity::getIsDelete, 1).eq(MerchantAccountEntity::getStatus, 1));
        } catch(Exception e) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }
        List<MerchantAccountDO> doList = getDomainList(list);

        if(doList == null) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.MERCHANT_LIST_ERROR);
            return result;
        }
        result.setModel(ResultDO.FIRST_MODEL_KEY, doList);
        return result;
    }

    @Transactional(propagation= Propagation.REQUIRED, rollbackFor= Exception.class)
    public ResultDO remove(String id, String modifier) {

        ResultDO result = new ResultSupport();

        MerchantAccountEntity entity = new MerchantAccountEntity();

        entity.setModifiDate(Calendar.getInstance().getTime());
        entity.setModifier(modifier);
        entity.setId(id);
        entity.setIsDelete(Constant.IS_DELETE);

        boolean res = false;
        try {
            res = this.updateById(entity);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }

        //判断r的值是否小于0.如果小于0 就代表添加失败
        if(!res) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.MERCHANT_ACCOUNT_DELETE_ERROR);
            logger.debug("error code：" + ErrorCode.MERCHANT_ACCOUNT_DELETE_ERROR.getCode() + " msg" + ErrorCode.MERCHANT_ACCOUNT_DELETE_ERROR.getMsg());
            return result;
        }
        redisUtil.del(Constant.MERCHANT_ACCOUNT + ":" + id);
        return result;
    }


    public ResultDO find(MerchantAccountQueryDO query) {
        MerchantAccountEntity entity = new MerchantAccountEntity();

        ResultDO result = BeanUtilsExtends.copy(entity,query);
        if (!result.isSuccess()) {
            logger.debug("error code：" + result.getErrorCode().getCode() + " msg" + result.getErrorCode().getMsg());
            return result;
        }
        QueryWrapper<MerchantAccountEntity> wrapper = new QueryWrapper<MerchantAccountEntity>(entity);

        Page<MerchantAccountEntity> page = new Page<>(query.getCurrentPage(),query.getPageSize());
        IPage<MerchantAccountEntity> iPage = null;
        try {
            iPage = baseMapper.selectPage(page, wrapper);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setErrorCode(ErrorCode.SYSTEM_EXCEPTION);
            logger.debug("error code：" + ErrorCode.SYSTEM_EXCEPTION.getCode() + " msg" + ErrorCode.SYSTEM_EXCEPTION.getMsg());
            return result;
        }

        List<MerchantAccountDO> doList = getDomainList(iPage.getRecords());

        PageDO<MerchantAccountDO> pageDO = new PageDO<>();

        pageDO.setCurrentPage(iPage.getCurrent());
        pageDO.setPageCount(iPage.getPages());
        pageDO.setPageSize(iPage.getSize());
        pageDO.setRowCount(iPage.getTotal());
        pageDO.setData(doList);

        result.setModel(ResultDO.FIRST_MODEL_KEY, pageDO);
        return result;
    }

    private MerchantAccountDO getDomain(MerchantAccountEntity entity) {

        //判断房源对象是否为空，为空就返回 空，不为空就进行下面的代码
        if(entity == null) return null;
        MerchantAccountDO domain = new MerchantAccountDO();

        domain = BeanUtilsExtends.copyProperties(domain, entity) ? domain : null;

        //判断拷贝数据到Dto对象是否成功，如果成功则返回domian，如果不成功就返回空
        return domain;
    }

    private List<MerchantAccountDO> getDomainList(List<MerchantAccountEntity> list) {
        //创建一个名为dolist的房源do类型的 list集合
        List<MerchantAccountDO> doList = new ArrayList<MerchantAccountDO>();

        //判断传入的list的集合的size是否大于0
        if(list.size() > 0) {
            //大于0是，遍历list，创建do类型的对象 调用getdomain方法
            for(MerchantAccountEntity entity : list) {

                MerchantAccountDO domain = this.getDomain(entity);

                //判断得到的domain 是否为空 不为空 就往dolist集合里面添加domian 为空就返回空
                if(domain == null) {
                    return null;
                }
                doList.add(domain);
            }
        } else {
            return null;
        }
        return doList;
    }



}
